package org.example.physics_engine.body;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.example.physics_engine.BasicSettings;
import org.example.physics_engine.Epsilon;
import org.example.physics_engine.geometry.Interval;
import org.example.physics_engine.geometry.Vector2d;
import org.example.physics_engine.geometry.shapes.AbstractShape;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*代表真实世界中的刚体*/
@Data
@RequiredArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class", visible = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class PhysicsBody {

    //物体质量
    protected Mass mass;
    //物体的线速度
    protected final Vector2d linearVelocity;
    //物体的角速度
    protected double angularVelocity;
    //线性阻尼，包括阻力
    protected double linearDamping;
    //物体所受合力
    protected final Vector2d resultantForce;
    //物体受力列表
    protected final List<Vector2d> forces;
    //物体组成部分，一个实体可以由多个形状构成
    protected final AbstractShape shape;
    //每个物体的重力比例系数
    protected double gravityScale;
    //阻力系数
    protected double friction;
    //回弹系数
    protected double restitution;
    //回弹速度
    protected double restitutionVelocity;

    public PhysicsBody(AbstractShape shape) {
        this.mass = new Mass();
        this.linearVelocity = new Vector2d();
        this.linearDamping = 0.0;
        this.resultantForce = new Vector2d();
        this.forces = new ArrayList<>();
        this.shape = shape;
        //设置重力加速度系数为10
        this.gravityScale = 10.0;
    }

    // 反序列化调用
    @Deprecated
    public PhysicsBody() {
        this(null);
    }

    //使用受力列表更新合力
    protected void updateForce() {
        resultantForce.zero();
        int num = forces.size();
        if (num > 0) {
            Iterator<Vector2d> it = forces.iterator();
            while (it.hasNext()) {
                resultantForce.add((Vector2d) it.next());
                it.remove();
            }
        }
    }

    //利用重力和受力列表更新速度
    public void integrateVelocity(Vector2d gravity) {
        if (this.mass.getType() == MassType.INFINITE || this.mass.getType() == null) {
            return;
        }
        double elapsedTime = BasicSettings.DEFAULT_STEP_FREQUENCY;
        this.updateForce();

        double mass = this.mass.getMass();
        double inverseMass = this.mass.getInverseMass();

        // 施加重力和引力
        if (inverseMass > Epsilon.E) {
            this.linearVelocity.x += elapsedTime * inverseMass * (gravity.x * this.gravityScale * mass + this.resultantForce.x);
            this.linearVelocity.y += elapsedTime * inverseMass * (gravity.y * this.gravityScale * mass + this.resultantForce.y);
        }

        // 施加线性阻尼
        if (this.linearDamping != 0.0) {
            double linear = 1.0 - elapsedTime * this.linearDamping;
            linear = Interval.clamp(linear, 0.0, 1.0);

            this.linearVelocity.x *= linear;
            this.linearVelocity.y *= linear;
        }

        // 施加转动阻尼
        double angular = 1.0 - elapsedTime * BasicSettings.DEFAULT_ANGULAR_DAMPING;
        angular = Interval.clamp(angular, 0.0, 1.0);

        this.angularVelocity *= angular;
    }

    //返回物体是否固定
    protected boolean isStatic() {
        return this.mass.getType() == MassType.INFINITE &&
                Math.abs(this.linearVelocity.x) <= Epsilon.E &&
                Math.abs(this.linearVelocity.y) <= Epsilon.E &&
                Math.abs(this.angularVelocity) <= Epsilon.E;
    }

    //使用速度更新位置
    public void integratePosition() {
        double elapsedTime = BasicSettings.DEFAULT_STEP_FREQUENCY;
        double maxTranslation = BasicSettings.DEFAULT_MAXIMUM_TRANSLATION;
        double maxTranslationSquared = maxTranslation * maxTranslation;
        double maxRotation = BasicSettings.DEFAULT_MAXIMUM_ROTATION;

        if (this.isStatic()) {
            return;
        }

        // 计算1tick内的平移量
        double translationX = this.linearVelocity.x * elapsedTime;
        double translationY = this.linearVelocity.y * elapsedTime;
        double translationMagnitudeSquared = translationX * translationX + translationY * translationY;

        // 确保平移量不超过限制
        if (translationMagnitudeSquared > maxTranslationSquared) {
            double translationMagnitude = Math.sqrt(translationMagnitudeSquared);
            double ratio = maxTranslation / translationMagnitude;

            this.linearVelocity.multiplyNum(ratio);
            translationX *= ratio;
            translationY *= ratio;
        }

        // 计算1tick内的旋转量
        double rotation = this.angularVelocity * elapsedTime;

        // 确保旋转量不超过限制
        if (rotation > maxRotation) {
            double ratio = maxRotation / Math.abs(rotation);
            this.angularVelocity *= ratio;
            rotation *= ratio;
        }

        // 应用平移量和旋转量
        this.getShape().translate(translationX, translationY);
        Vector2d center = this.getShape().getTransform().getTransformed(this.getMass().getCenter());
        this.getShape().rotate(rotation, center.x, center.y);
    }

}
